<?php if (!defined('BASEPATH')) exit('No direct script access allowed');
class Deploy
{
	private $db = null;
	private $CI = null;
	
	private $applib = null;
	
	private $insert = array(); // 新增文件
	private $update = array(); // 变更文件
	private $record = array(); // 记录所有文件
	
	private $_path = array();
	private $applibentry_id = null;
	
	private $_params = array();
	private $errmsg = array();
	
	// 版本差异数据
	private $applib_diff = null;
	
	public function __construct($params)
	{
		$this->CI =& get_instance();
		$this->db = $this->CI->db;
		$this->_path = config_item('path');
		$this->_params = $params;
		// 获取策略信息
		$this->getAppLib($this->_params['applibentry_id']);	
		
		$this->CI->load->library('Sftp', array('host'=>$this->_params['host']));
		try 
		{
		    $this->CI->sftp->login($this->_params['user'], $this->_params['password']);
		}
		catch (Exception $e) 
		{
		    exit($e->getMessage()); 
		}	
	}
	
	public function deployApp()
	{
		// 如果是第一次部署，上传版本文件及配置文件。
		if ($this->applib['state'] == 0 && empty($this->applib['deploy_at']))
		{
			$this->errmsg[] = "第一次部署 ".$this->applib['app_id'].' '.$this->applib['edition_id'];
			// 打包整个版本数据			
			$this->CI->load->library("HZip");				
			// 本地/远程 部署目录/{app_id}_{edition_id}_{applibentry_id}_{applibentry_ver}.zip
			$outzip = sprintf("%s_%s_%d_%d.zip", $this->applib['app_id'], $this->applib['edition_id'],  $this->applib['applibentry_id'], $this->applib['applibentry_ver']);
			// 应用版本根目录
			$app_dir = get_app_version_dir($this->applib['app_id'], $this->applib['edition_id']);			
			if (! file_exists($this->_path['local_deploy'].$outzip)) 
			{
				HZip::zipDir($app_dir, $this->_path['local_deploy'].$outzip);				
			}
			$this->errmsg[] = "生成压缩版本文件 ".$this->_path['local_deploy'].$outzip;			
			$this->uploadToServer($outzip);
			
			// 设置部署状态
			$this->db->where("applibentry_id", $this->applib['applibentry_id']);
			$this->db->update("applibraryentries_tbl", array("state"=>1, "deploy_at"=>date("Y-m-d H:i:s")));
			$this->errmsg[] = "部署成功";
			
			return $this->return_errmsg();
		}
		else
		{
			$this->finddiff();	
			$this->sync();		
			$this->sync_applib_version();		
			$this->saveDeployFile();
			$this->deployToServer();
			$this->return_errmsg();
		}		
	}
	
	// @todo 需要错误处理
	private function return_errmsg()
	{
		return implode("<br />", $this->errmsg);
	}
	
	private function getAppLib($id)
	{
		$query = $this->db->query("SELECT * FROM applibraryentries_tbl WHERE applibentry_id = $id");
		$this->applib = $query->row_array();	
	}
	
	//同步策略中应用的版本文件与应用版本的文件
	//如果已经部署则打包差异版本文件	
	public function sync()
	{				
		$applib_id = $this->applib['applibrary_id'];		
		// 生成一个差异化版本文件zip
		// 是否有差异版本，直接替换
		if (!empty($this->update)) 
		{
			$size = count($this->update);
			for($i=0; $i<$size; $i++)
			{
				// 获得数据
				$file_id = $this->update[$i];
				$query = $this->db->query("SELECT * FROM appfiles_tbl WHERE file_id = $file_id");
				$appfile = $query->row_array();
				$appfile['file_original_version'] = $appfile['file_version'];
				$appfile['applib_id'] = $applib_id;
				unset($appfile['file_id']);
				
				$this->db->where("file_id", $file_id);
				$this->db->update("applibentryfiles_tbl", $appfile);	
			}	
		}

		// 是否有新增文件，直接添加
		if (!empty($this->insert)) 
		{
			$size = count($this->insert);
			for($i=0; $i<$size; $i++)
			{
				$file_id = intval($this->insert[$i]);
				$query = $this->db->query("SELECT * FROM appfiles_tbl WHERE file_id = $file_id");
				$appfile = $query->row_array();
				$appfile['file_original_version'] = $appfile['file_version'];
				$appfile['applib_id'] = $applib_id;				
				$this->db->insert("applibentryfiles_tbl", $appfile);					
			}	
		}						
	}
	
	//找出策略中应用的版本文件与应用版本的文件的差异版本文件
	public function finddiff()
	{
		$record = array('diff'=>array(), 'all'=>array()); // 记录策略应用的文件id
		// @todo applibraryentries_tbl 为什么不直接使用applib_id做主键
		// 检查是否有不一致的文件版本
		$sql = "SELECT file_id, file_version, file_original_version FROM applibentryfiles_tbl WHERE app_id = '%s' AND edition_id = '%s'  AND applib_id = '%d'";		
			
		// 比较版本差异	
		$query = $this->db->query(sprintf($sql, $this->applib['app_id'], $this->applib['edition_id'], intval($this->applib['applibrary_id'])));
		if ($query->num_rows() > 0)
		{
			foreach($query->result_array() as $row)
			{
				$record['all'][] = $row['file_id'];
				// 比较版本
				if ($row['file_version'] != $row['file_original_version']) 
				{
					$record['diff'][] = $row['file_id'];
				}
			}
		}
		
		$sql = "SELECT file_id, file_version, file_name, file_path FROM appfiles_tbl WHERE app_id = '%s' AND edition_id = '%s'";
		$query = $this->db->query(sprintf($sql, $this->applib['app_id'], $this->applib['edition_id']));
		if ($query->num_rows() > 0)
		{
			foreach($query->result_array() as $row)
			{
				// 找出是否有新增的文件
				if (! in_array($row['file_id'], $record['all'])) 
				{
					$this->insert[] = $row['file_id'];					
					$this->record[] = $row['file_id'];
				}
				// 找出差异的文件版本	
				if (in_array($row['file_id'], $record['diff'])) 
				{
					$this->update[] = $row['file_id'];
					$this->record[] = $row['file_id'];
				}		
			}
		}
	}	
	
	// 同步策略版本
	public function sync_applib_version()
	{		
		$query = $this->db->query("SELECT edition_ver FROM appeditions_tbl WHERE app_id = '{$this->applib['app_id']}' AND edition_id = '{$this->applib['edition_id']}'");
		$edition = $query->row_array();			
		$this->db->set("applibentry_ver", "applibentry_ver+1", false);
		$this->db->set("edition_ver", $edition['edition_ver'], false);
		$this->db->where("applibentry_id", $this->applib['applibentry_id']);
		$this->db->update("applibraryentries_tbl");
	}
	
	// 发布到服务器
	public function deployToServer()
	{
		$zip_file = sprintf("%s_%s_%d_%d.zip", $this->applib['app_id'], $this->applib['edition_id'], $this->applib['applibentry_id'], $this->applib['applibentry_ver']);
		$local_deploy_zip = $this->_path['local_deploy'].$zip_file;								
		if (file_exists($local_deploy_zip))
		{
			$this->uploadToServer($zip_file);
		}	
		else
		{
			$zip = new ZipArchive;						
			// @todo 获取最新版本需要更新的文件，这个版本还不是增量更新，但是只更新少量的文件。		
			if ($zip->open($local_deploy_zip, ZipArchive::CREATE) === TRUE) 
			{
				$sql = "SELECT file_name, max(applib_version) as max_version, file_content FROM appdeploy_tbl GROUP BY file_id";
				$query = $this->db->query($sql);
				if ($query->num_rows() > 0)
				{
					foreach($query->result_array() as $row)
					{
						$zip->addFromString($row['file_name'], $row['file_content']);
					}
				}
			}
			$zip->close();	
			$this->uploadToServer($zip_file);							
		}		
	}
	
	// 上传版本到服务器
	private function uploadToServer($zip_file)
	{		
		$this->sftp($this->_path['local_deploy'].$zip_file, $this->_params['deploy'].$zip_file);								
		// 创建配置文件			
		$this->sftp($this->createConfigFile(), $this->_params['deploy']."appconfig.php");			
	}
	
	// 保存每次版本更新的文件数据
	public function saveDeployFile()
	{
		// 重新获取applib
		$this->getAppLib($this->_params['applibentry_id']);
		$flag = false;
		
		if (!empty($this->record))
		{								
			foreach($this->record as $file_id)
			{
				$file = $this->getAppfileById(intval($file_id));
				// 保存文件至发布数据库
				$save_deploy_file = array(
					'file_id'=>$file_id,
					'app_id'=>$this->applib['app_id'],
					'edition_id'=>$this->applib['edition_id'],
					'applib_version'=>$this->applib['applibentry_ver'],
					'applib_id'=>$this->applib['applibrary_id'],
					'file_name'=>$file['file_path'],
					'file_content'=> $file['file_content']
				);		
				$this->db->insert("appdeploy_tbl", $save_deploy_file);					
				if ($flag) 
				{
					$zip->addFromString($file['file_path'], $file['file_content']);	
				}		
			}									
		}		
	}
	
	// 生成配置文件，上传到服务器
	private function createConfigFile()
	{
$template =<<<HTML
<?php \$App = array(
	'%s' => array('app_id'=>'%s', 'edition_id'=>'%s', 'applibentry_id'=>%d, 'applib_ver'=>%d)
);						
HTML;
		$temp = $this->_path['local_version']."applib.cache";
		file_put_contents($temp, sprintf($template, $this->applib['app_id'], $this->applib['app_id'], $this->applib['edition_id'], intval($this->applib['applibentry_id']), intval($this->applib['applibentry_ver'])));
		return $temp;			
	}
	
	private function getAppfileById($file_id)
	{
		$query = $this->db->query(sprintf("SELECT * FROM appfiles_tbl WHERE file_id = %d", intval($file_id)));
		return $query->row_array();
	}
	
	//sftp上传文件到服务器
	private function sftp($localzip, $targetzip)
	{	
		$this->errmsg[] = "上传本地 $localzip 至服务器 $targetzip";		
		$this->CI->sftp->uploadFile($localzip, $targetzip);
	}	
}
?>
